#include "totaltest.h"
#include <QApplication>
#include <QByteArray>
#include <QDebug>
#include <QElapsedTimer>
#include <QString>
#include <QTest>
#include <QWidget>

#include <QtCore/qvarlengtharray.h>
#include <QtCore/qvector.h>
#include <QtQuickTest/quicktest.h>

namespace TotalTest {
typedef QList<TestCreatorBase *> TestList;
typedef QList<QString> QuickTestList;
const QString OUTPUT_DIR = "/data/storage/el2/base/haps/entry/files/";

TestList &testList() {
    static TestList list;
    return list;
}

QuickTestList &quickTestList() {
    static QuickTestList list;
    return list;
}

bool findTest(TestCreatorBase *test) {
    for (auto item : testList()) {
        if (item->name() == test->name())
            return true;
    }
    return false;
}

void addTest(TestCreatorBase *creator) {
    if (!findTest(creator)) {
        testList().append(creator);
    }
}

void addQuickTest(const QString &str) {
    if (!quickTestList().contains(str)) {
        quickTestList().append(str);
    }
}

QStringList getBaseArguments(int argc, char *argv[]) {
    QStringList args;
    args.reserve(argc + 1);
    for (int i = 0; i < argc; ++i) {
        args.append(QString::fromUtf8(argv[i]));
    }
    args.append("-o");
    return args;
}

int executeTest(const QStringList &arguments, std::function<int(int, char **)> executor) {
    QVector<QByteArray> argvBuffers;
    QVarLengthArray<char *> argvPtrs(arguments.size());

    argvBuffers.reserve(arguments.size());
    for (int i = 0; i < arguments.size(); ++i) {
        argvBuffers.append(arguments[i].toLocal8Bit());
        argvPtrs[i] = argvBuffers[i].data();
    }

    return executor(argvPtrs.size(), argvPtrs.data());
}

void writeTestProgress(int current, int total, const QString &testName, bool isQuickTest = false) {
    QFile file(OUTPUT_DIR + "qttest_progress.txt");
    if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
        QTextStream stream(&file);
        QString testType = isQuickTest ? "Quick Test" : "Test";
        stream
            << QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss] ")
            << QString("%1 Progress: %2/%3, Current Target: %4\n").arg(testType).arg(current).arg(total).arg(testName);
        file.close();
    }
}

void writeLog(const QString &log) {
    QFile file(OUTPUT_DIR + "qttest_progress.txt");
    if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
        QTextStream stream(&file);
        stream << QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss] ") << log << "\n";
        file.close();
    }
}

void cleanupEventQueue() {
    if (QCoreApplication::instance()) {
        QCoreApplication::removePostedEvents(nullptr);
    }
}

int runQuickTest(int argc, char *argv[]) {
    QElapsedTimer timer;
    timer.start();

    const auto &testCases = quickTestList();
    if (testCases.empty())
        return 0;

    QStringList baseArgs = getBaseArguments(argc, argv);
    int ret = 0;
    const int totalCases = testCases.size();

    for (int idx = 0; auto &target : testCases) {
        QStringList currentArgs = baseArgs;
        currentArgs.append((OUTPUT_DIR + "%1.xml,xml").arg(target));
        writeTestProgress(idx + 1, totalCases, target, true);
        qWarning() << QString("Quick Test Progress：%1/%2, Now Test Target：%3").arg(++idx).arg(totalCases).arg(target);

        ret += executeTest(currentArgs, [&](int argc, char **argv) {
            return quick_test_main(argc, argv, target.toLatin1().constData(), nullptr);
        });
    }

    qint64 elapsed = timer.elapsed();
    writeLog(QString("All Quick Tests (%1 cases) completed in %2 ms").arg(totalCases).arg(elapsed));
    qWarning() << QString("All Quick Tests (%1 cases) completed in %2 ms").arg(totalCases).arg(elapsed);
    qWarning() << "runQuickTest return------>";
    return ret;
}

int run(int argc, char *argv[], int flags, int startPos) {
    QElapsedTimer timer;
    timer.start();

    const auto &testCases = testList();
    if (testCases.empty())
        return 0;

    QStringList baseArgs = getBaseArguments(argc, argv);
    int ret = 0;
    const int totalCases = testCases.size();
    qputenv("QTDIR", "/data/storage/el2/base/testDir/qtDir");

    const int startIdx = (startPos > totalCases || startPos <= 0) ? 0 : startPos - 1;
    const int actualCount = totalCases - startIdx;

    for (int i = startIdx; i < totalCases; ++i) {
        std::unique_ptr<QCoreApplication> app;
        if (!qApp) {
            switch (flags) {
            case 1:
                app = std::make_unique<QApplication>(argc, argv);
                break;
            case 2:
                app = std::make_unique<QGuiApplication>(argc, argv);
                break;
            case 3:
                app = std::make_unique<QCoreApplication>(argc, argv);
                break;
            }
            if (app)
                app->setAttribute(Qt::AA_Use96Dpi, true);
        }
        
        auto test = testCases.at(i);
        QString testName = test->name();
        QStringList currentArgs = baseArgs;
        currentArgs.append((OUTPUT_DIR + "%1.xml,xml").arg(testName));
        QObject *testObject = test->createTest();
        writeTestProgress(i + 1, totalCases, testName);
        qWarning() << QString("Test Progress：%1/%2, Now Test Target：%3").arg(i + 1).arg(totalCases).arg(testName);
        
        ret += executeTest(currentArgs, [&](int argc, char **argv) { return QTest::qExec(testObject, argc, argv); });

        testObject->deleteLater();
        // 清理事件队列
        cleanupEventQueue();
    }

    qint64 elapsed = timer.elapsed();
    writeLog(QString("All Tests (%1 cases) completed in %2 ms").arg(actualCount).arg(elapsed));
    qWarning() << QString("All Tests (%1 cases) completed in %2 ms").arg(actualCount).arg(elapsed);
    qWarning() << "run return------>";
    return ret;
}

int execTestTask(int argc, char *argv[], int flags) {

    QFile::remove(OUTPUT_DIR + "qttest_progress.txt");
    
    qputenv("QTEST_FUNCTION_TIMEOUT", QString::number(30*60*1000).toLocal8Bit());

    std::unique_ptr<QApplication> appGuard;
    std::unique_ptr<QWidget> widget;
    // gui程序需要创建1个初始widget，防止测试项的widget销毁时，退出程序
    if (flags == 1 || flags == 2) {
        appGuard = std::make_unique<QApplication>(argc, argv);
        appGuard->setAttribute(Qt::AA_Use96Dpi, true);
        widget = std::make_unique<QWidget>();
        widget->setWindowTitle("guardWindow");
        widget->show();
    }

    // 默认为1
    int startPos = 1;
    return run(argc, argv, flags, startPos) + runQuickTest(argc, argv);
}

} // namespace TotalTest